﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using System;

namespace Utility.Graphic
{
	public class MashInstanceDrawer
	{
		const int BatchSize = 1023;

		public Mesh mesh = null;
		public Material material = null;
		public MaterialPropertyBlock properties = null;
		public ShadowCastingMode castShadows = ShadowCastingMode.On;
		public bool recieveShadows = true;
		public int layer = 0;
		public Camera camera = null;
		public LightProbeUsage lightProbeUsage = LightProbeUsage.BlendProbes;
		public LightProbeProxyVolume lightProbeProxyVolume = null;
		public Matrix4x4 originalMatrix4x4 = Matrix4x4.identity;
		public int fogGoIndex = 0;

		List<Matrix4x4[]> matricesList = new List<Matrix4x4[]> ();
		int batchIndex = 0;
		int perBatchIndex = 0;
		Matrix4x4[] array;

		public MashInstanceDrawer ()
		{
			Clear ();
		}

		public void Clear ()
		{
			batchIndex = 0;
			perBatchIndex = 0;
			array = GetArray (0);
		}

		public void AddMatrix (Matrix4x4 matrix)
		{
			if (perBatchIndex >= BatchSize)
			{
				batchIndex++;
				perBatchIndex = 0;
				array = GetArray (batchIndex);
			}

			array[perBatchIndex] = matrix;
			perBatchIndex++;
		}

		public void AddMatrices (IEnumerable<Matrix4x4> matrices)
		{
			foreach (var matrix in matrices)
			{
				AddMatrix (matrix);
			}
		}

		public void AddMatrices<T> (IEnumerable<T> sources, Func<T, Matrix4x4> matrixGetter)
		{
			foreach (var source in sources)
			{
				AddMatrix (matrixGetter (source) * originalMatrix4x4);
			}
		}

		public void AddMatrices<T> (IEnumerable<T> sources, Func<T, Matrix4x4?> matrixGetter)
		{
			foreach (var source in sources)
			{
				var m = matrixGetter (source);
				if (m.HasValue)
				{
					AddMatrix (m.Value * originalMatrix4x4);
				}
			}
		}

		public void Draw ()
		{
			for (int i = 0; i <= batchIndex; i++)
			{
				int count = i == batchIndex ? perBatchIndex : BatchSize;
				Graphics.DrawMeshInstanced (mesh, 0, material, matricesList[i], count, properties, castShadows, recieveShadows, layer, camera, lightProbeUsage, lightProbeProxyVolume);
			}
		}

		public void Draw (IEnumerable<Matrix4x4> matrices)
		{
			Clear ();
			AddMatrices (matrices);
			Draw ();
		}

		public void Draw<T> (IEnumerable<T> sources, Func<T, Matrix4x4> matrixGetter)
		{
			Clear ();
			AddMatrices (sources, matrixGetter);
			Draw ();
		}

		public void Draw<T> (IEnumerable<T> sources, Func<T, Matrix4x4?> matrixGetter)
		{
			Clear ();
			AddMatrices (sources, matrixGetter);
			Draw ();
		}

		Matrix4x4[] GetArray (int batchIndex)
		{
			Matrix4x4[] array = null;
			if (matricesList.Count <= batchIndex)
			{
				array = new Matrix4x4[BatchSize];
				matricesList.Add (array);
			}
			return matricesList[batchIndex];
		}

	}
}